@objectstack/runtime 7.0.0 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1709,7 +1709,8 @@ var init_app_plugin = __esm({
1709
1709
  "translations",
1710
1710
  "sharingRules",
1711
1711
  "ragPipelines",
1712
- "data"
1712
+ "data",
1713
+ "emailTemplates"
1713
1714
  ];
1714
1715
  const hasAppPayload = APP_CATEGORY_KEYS.some((k) => {
1715
1716
  const v = (bundle && bundle[k]) ?? (sys && sys[k]);
@@ -8474,6 +8475,36 @@ function resolveCloudUrl(explicit) {
8474
8475
  return picked.replace(/\/+$/, "");
8475
8476
  }
8476
8477
 
8478
+ // src/cloud/marketplace-public-url.ts
8479
+ function resolveMarketplacePublicBaseUrl(explicit) {
8480
+ const raw = (explicit ?? process.env.OS_MARKETPLACE_PUBLIC_BASE_URL ?? "").trim();
8481
+ const lower = raw.toLowerCase();
8482
+ if (!raw || lower === "off" || lower === "none" || lower === "disabled" || lower === "false") {
8483
+ return "";
8484
+ }
8485
+ return raw.replace(/\/+$/, "");
8486
+ }
8487
+ function publicMarketplaceKeyForApiPath(pathname) {
8488
+ const prefix = "/api/v1/marketplace/packages";
8489
+ if (pathname === prefix) return "packages.json";
8490
+ if (!pathname.startsWith(`${prefix}/`)) return null;
8491
+ const tail = pathname.slice(prefix.length + 1);
8492
+ if (!tail) return null;
8493
+ const parts = tail.split("/");
8494
+ if (parts.length === 1) {
8495
+ const id = decodeURIComponent(parts[0] ?? "");
8496
+ if (!id) return null;
8497
+ return `packages/${encodeURIComponent(id)}.json`;
8498
+ }
8499
+ if (parts.length === 4 && parts[1] === "versions" && parts[3] === "manifest") {
8500
+ const id = decodeURIComponent(parts[0] ?? "");
8501
+ const versionId = decodeURIComponent(parts[2] ?? "");
8502
+ if (!id || !versionId) return null;
8503
+ return `packages/${encodeURIComponent(id)}/versions/${encodeURIComponent(versionId)}/manifest.json`;
8504
+ }
8505
+ return null;
8506
+ }
8507
+
8477
8508
  // src/cloud/marketplace-proxy-plugin.ts
8478
8509
  var MARKETPLACE_PREFIX = "/api/v1/marketplace";
8479
8510
  var DEFAULT_LRU_MAX = 200;
@@ -8513,7 +8544,7 @@ var LruTtlCache = class {
8513
8544
  var MarketplaceProxyPlugin = class _MarketplaceProxyPlugin {
8514
8545
  constructor(config = {}) {
8515
8546
  this.name = "com.objectstack.runtime.marketplace-proxy";
8516
- this.version = "1.0.0";
8547
+ this.version = "1.1.0";
8517
8548
  this.init = async (_ctx) => {
8518
8549
  };
8519
8550
  this.start = async (ctx) => {
@@ -8531,7 +8562,11 @@ var MarketplaceProxyPlugin = class _MarketplaceProxyPlugin {
8531
8562
  }
8532
8563
  const rawApp = httpServer.getRawApp();
8533
8564
  const cloudUrl = this.cloudUrl;
8565
+ const publicBaseUrl = this.publicBaseUrl;
8534
8566
  const cache = this.cache;
8567
+ if (publicBaseUrl) {
8568
+ ctx.logger?.info?.(`[MarketplaceProxyPlugin] public R2 fast-path enabled \u2192 ${publicBaseUrl}`);
8569
+ }
8535
8570
  const handler = async (c, next) => {
8536
8571
  if (!cloudUrl) {
8537
8572
  return c.json({
@@ -8547,8 +8582,18 @@ var MarketplaceProxyPlugin = class _MarketplaceProxyPlugin {
8547
8582
  if (incomingUrl.pathname.startsWith(`${MARKETPLACE_PREFIX}/install-local`)) {
8548
8583
  return next();
8549
8584
  }
8550
- const target = `${cloudUrl}${incomingUrl.pathname}${incomingUrl.search}`;
8551
8585
  const method = String(c.req.method ?? "GET").toUpperCase();
8586
+ if (publicBaseUrl && (method === "GET" || method === "HEAD")) {
8587
+ const r2Resp = await tryPublicMarketplaceFetch(
8588
+ publicBaseUrl,
8589
+ incomingUrl,
8590
+ method,
8591
+ c.req.header("accept"),
8592
+ ctx.logger
8593
+ );
8594
+ if (r2Resp) return r2Resp;
8595
+ }
8596
+ const target = `${cloudUrl}${incomingUrl.pathname}${incomingUrl.search}`;
8552
8597
  if (method !== "GET" && method !== "HEAD") {
8553
8598
  return c.json({
8554
8599
  success: false,
@@ -8629,12 +8674,82 @@ var MarketplaceProxyPlugin = class _MarketplaceProxyPlugin {
8629
8674
  });
8630
8675
  };
8631
8676
  this.cloudUrl = resolveCloudUrl(config.controlPlaneUrl);
8677
+ this.publicBaseUrl = resolveMarketplacePublicBaseUrl(config.publicMarketplaceBaseUrl);
8632
8678
  const envFlag = (process.env.OS_MARKETPLACE_CACHE ?? "").trim().toLowerCase();
8633
8679
  const envDisabled = ["off", "false", "0", "no", "disable", "disabled"].includes(envFlag);
8634
8680
  const disabled = config.cacheDisabled ?? envDisabled;
8635
8681
  this.cache = disabled ? null : new LruTtlCache(Math.max(8, config.cacheMaxEntries ?? DEFAULT_LRU_MAX));
8636
8682
  }
8637
8683
  };
8684
+ async function tryPublicMarketplaceFetch(publicBaseUrl, incomingUrl, method, acceptHeader, logger) {
8685
+ const key = publicMarketplaceKeyForApiPath(incomingUrl.pathname);
8686
+ if (!key) return null;
8687
+ const target = `${publicBaseUrl}/${key}`;
8688
+ let resp;
8689
+ try {
8690
+ resp = await fetch(target, {
8691
+ method: "GET",
8692
+ headers: {
8693
+ "Accept": acceptHeader || "application/json",
8694
+ "User-Agent": `objectos-marketplace-proxy/public-r2`
8695
+ }
8696
+ });
8697
+ } catch (err) {
8698
+ logger?.warn?.(`[MarketplaceProxyPlugin] public R2 fetch failed (${target}): ${err?.message ?? err}`);
8699
+ return null;
8700
+ }
8701
+ if (resp.status === 404) return null;
8702
+ if (!resp.ok) {
8703
+ logger?.warn?.(`[MarketplaceProxyPlugin] public R2 ${target} returned ${resp.status} \u2014 falling back to cloud`);
8704
+ return null;
8705
+ }
8706
+ const isList = key === "packages.json";
8707
+ const hasFilters = isList && (incomingUrl.searchParams.has("q") || incomingUrl.searchParams.has("category") || incomingUrl.searchParams.has("limit") || incomingUrl.searchParams.has("offset"));
8708
+ if (!hasFilters) {
8709
+ const headers2 = new Headers();
8710
+ const ct = resp.headers.get("content-type") ?? "application/json; charset=utf-8";
8711
+ headers2.set("content-type", ct);
8712
+ const cc = resp.headers.get("cache-control");
8713
+ if (cc) headers2.set("cache-control", cc);
8714
+ const etag = resp.headers.get("etag");
8715
+ if (etag) headers2.set("etag", etag);
8716
+ headers2.set("x-cache", "PUBLIC-R2");
8717
+ const body2 = method === "HEAD" ? null : resp.body;
8718
+ return new Response(body2, { status: 200, headers: headers2 });
8719
+ }
8720
+ let snapshot;
8721
+ try {
8722
+ snapshot = await resp.json();
8723
+ } catch (err) {
8724
+ logger?.warn?.(`[MarketplaceProxyPlugin] public R2 list snapshot parse failed: ${err?.message ?? err}`);
8725
+ return null;
8726
+ }
8727
+ const items = Array.isArray(snapshot?.data?.items) ? snapshot.data.items : [];
8728
+ const q = (incomingUrl.searchParams.get("q") ?? "").trim().toLowerCase();
8729
+ const category = (incomingUrl.searchParams.get("category") ?? "").trim();
8730
+ const limit = Math.min(Math.max(Number(incomingUrl.searchParams.get("limit") ?? 50), 1), 100);
8731
+ const offset = Math.max(Number(incomingUrl.searchParams.get("offset") ?? 0), 0);
8732
+ let filtered = items;
8733
+ if (q) {
8734
+ filtered = filtered.filter((r) => {
8735
+ const dn = String(r?.display_name ?? "").toLowerCase();
8736
+ const mid = String(r?.manifest_id ?? "").toLowerCase();
8737
+ return dn.includes(q) || mid.includes(q);
8738
+ });
8739
+ }
8740
+ if (category) {
8741
+ filtered = filtered.filter((r) => String(r?.category ?? "") === category);
8742
+ }
8743
+ const total = filtered.length;
8744
+ const page = filtered.slice(offset, offset + limit);
8745
+ const body = JSON.stringify({ success: true, data: { items: page, total, limit, offset } });
8746
+ const headers = new Headers({
8747
+ "content-type": "application/json; charset=utf-8",
8748
+ "cache-control": "public, max-age=30",
8749
+ "x-cache": "PUBLIC-R2-FILTERED"
8750
+ });
8751
+ return new Response(method === "HEAD" ? null : body, { status: 200, headers });
8752
+ }
8638
8753
  var PASSTHROUGH_HEADERS = ["content-type", "cache-control", "etag", "last-modified", "vary"];
8639
8754
  function collectHeaders(src) {
8640
8755
  const out = {};
@@ -9118,22 +9233,55 @@ var MarketplaceInstallLocalPlugin = class {
9118
9233
  return c.json({ success: false, error: { code: "bad_request", message: "packageId is required." } }, 400);
9119
9234
  }
9120
9235
  let payload;
9121
- try {
9122
- const url = `${this.cloudUrl}/api/v1/marketplace/packages/${encodeURIComponent(packageId)}/versions/${encodeURIComponent(versionId)}/manifest`;
9123
- const resp = await fetch(url, { headers: { "Accept": "application/json" } });
9124
- if (!resp.ok) {
9125
- const text = await resp.text().catch(() => "");
9236
+ const publicBase = resolveMarketplacePublicBaseUrl();
9237
+ const fetchAttempts = [];
9238
+ if (publicBase) {
9239
+ fetchAttempts.push({
9240
+ label: "public-r2",
9241
+ url: `${publicBase}/packages/${encodeURIComponent(packageId)}/versions/${encodeURIComponent(versionId)}/manifest.json`
9242
+ });
9243
+ }
9244
+ fetchAttempts.push({
9245
+ label: "cloud",
9246
+ url: `${this.cloudUrl}/api/v1/marketplace/packages/${encodeURIComponent(packageId)}/versions/${encodeURIComponent(versionId)}/manifest`
9247
+ });
9248
+ let lastErrStatus = 0;
9249
+ let lastErrText = "";
9250
+ for (const attempt of fetchAttempts) {
9251
+ try {
9252
+ const resp = await fetch(attempt.url, { headers: { "Accept": "application/json" } });
9253
+ if (!resp.ok) {
9254
+ lastErrStatus = resp.status;
9255
+ lastErrText = (await resp.text().catch(() => "")).slice(0, 200);
9256
+ if (attempt.label === "public-r2" && resp.status === 404) {
9257
+ ctx.logger?.info?.(`[MarketplaceInstallLocal] public-r2 miss for ${packageId}@${versionId}, falling back to cloud`);
9258
+ continue;
9259
+ }
9260
+ if (attempt.label === "public-r2" && resp.status >= 500) {
9261
+ ctx.logger?.warn?.(`[MarketplaceInstallLocal] public-r2 ${resp.status}, falling back to cloud`);
9262
+ continue;
9263
+ }
9264
+ break;
9265
+ }
9266
+ payload = await resp.json();
9267
+ lastErrStatus = 0;
9268
+ break;
9269
+ } catch (err) {
9270
+ if (attempt.label === "public-r2") {
9271
+ ctx.logger?.warn?.(`[MarketplaceInstallLocal] public-r2 fetch error: ${err?.message ?? err}, falling back to cloud`);
9272
+ continue;
9273
+ }
9126
9274
  return c.json({
9127
9275
  success: false,
9128
- error: { code: "cloud_fetch_failed", message: `Cloud returned ${resp.status}: ${text.slice(0, 200)}` }
9129
- }, resp.status === 404 ? 404 : 502);
9276
+ error: { code: "cloud_fetch_failed", message: err?.message ?? String(err) }
9277
+ }, 502);
9130
9278
  }
9131
- payload = await resp.json();
9132
- } catch (err) {
9279
+ }
9280
+ if (!payload) {
9133
9281
  return c.json({
9134
9282
  success: false,
9135
- error: { code: "cloud_fetch_failed", message: err?.message ?? String(err) }
9136
- }, 502);
9283
+ error: { code: "cloud_fetch_failed", message: `Cloud returned ${lastErrStatus}: ${lastErrText}` }
9284
+ }, lastErrStatus === 404 ? 404 : 502);
9137
9285
  }
9138
9286
  const data = payload?.data ?? payload;
9139
9287
  const manifest = data?.manifest;